package com.igoal.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.util.Assert;

import com.igoal.dao.GenericDao;
import com.igoal.dao.UserDAOExt;
import com.igoal.dto.BaseCommentDTO;
import com.igoal.dto.UserDTO;
import com.igoal.entity.ActionTypeEnum;
import com.igoal.entity.ICommentEntry;
import com.igoal.entity.ComponentEnum;
import com.igoal.entity.DataStatus;
import com.igoal.entity.Entry;
import com.igoal.entity.User;
import com.igoal.entity.UserGoalDone;

public abstract class BaseCommentEntryService<E extends Entry, C extends ICommentEntry<E>, D extends BaseCommentDTO<E, C>> 
	extends BaseService<D, C, Long>{
	protected Log logger = LogFactory.getLog(getClass());

	protected GenericDao<C, Long> commentDAO;
	protected GenericDao<E, Long> entryDAO;	
	@Qualifier("userDAOExt")
	protected UserDAOExt userDAO;
	
	protected ActionLoggerService actionLoggerService;

	public GenericDao<C, Long> getCommentDAO() {
		return commentDAO;
	}

	public void setCommentDAO(GenericDao<C, Long> commentDAO) {
		this.commentDAO = commentDAO;
	}

	public ActionLoggerService getActionLoggerService() {
		return actionLoggerService;
	}

	public void setActionLoggerService(ActionLoggerService actionLoggerService) {
		this.actionLoggerService = actionLoggerService;
	}

	public D addCommentToGoalEntry(D dto, UserDTO user) {
		try {
			if (dto == null || StringUtils.isBlank(dto.getComment()))
				return null;

			E entry = entryDAO.get(dto.getEntryId());
			Assert.notNull(entry, "Entry is not exists.");
			if (DataStatus.DELETED.equals(entry.getStatus())) {
				logger.info("User requested add comment to a deleted entry.");
				return null;
			}

			User userEntity = userDAO.get(user.getId());
			Assert.notNull(entry, "User of comment is not exists.");
			
			C comment = initEntity();
			comment.setUser(userEntity);
			comment.setEntry(entry);
			
			if (logger.isDebugEnabled()) {
				logger.debug("User[" + user.getUsername() + "] add comment["
						+ dto.getComment() + "] to entry[" + entry.getTitle()
						+ "]");
			}

			if (dto.getParentId() != null) {
				C parentComment = commentDAO.get(dto.getParentId());
				comment.setParent(parentComment);
			}

			dto.setComment(com.igoal.utils.StringUtils.removeHTMLTags(dto.getComment()));
			dto.copyAttributesTo(comment);

			entry.setCommentCount(entry.getCommentCount() + 1);
			entryDAO.save(entry);
			comment = commentDAO.save(comment);
			if (comment == null) {
				if(logger.isDebugEnabled()){
					logger.debug("Cannot add the comment from user.");
				}
				return null;
			} else {
				if(logger.isDebugEnabled()){
					logger.debug("Comment is added successfully.");
				}

				updateEntity(userEntity);
				userDAO.save(userEntity);
				
				ComponentEnum component = ComponentEnum.GOAL_ENTRY;
				if(comment instanceof UserGoalDone){
					component = ComponentEnum.DONE_ENTRY;
				}
				actionLoggerService.log(entry.getUser(), userEntity,
						ActionTypeEnum.COMMENT, component, entry
								.getId());

				dto.initFrom(comment);
				return dto;
			}
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}

	public int deleteCommentOfGoalEntry(D dto, UserDTO whoDelete) {
		if (dto == null || whoDelete == null)
			return SaveResult.FAIL;
		try {
			C comment = commentDAO.get(dto.getId());
			Assert.notNull(comment, "Data failure.");

			E entry = comment.getEntry();
			User entryOwner = entry.getUser();
			User owner = comment.getUser();
			if (!whoDelete.getId().equals(entryOwner.getId())
					&& !whoDelete.getId().equals(owner.getId()) && !isAdministrator(whoDelete)) {
				logger.info("The session user[" + whoDelete.getUsername()
						+ "] has not permission to delete the comment of entry[" + entry.getTitle()
						+ "]");
				return SaveResult.FAIL;
			}
			comment.setUpdateDate(new Date());
			comment.setStatus(DataStatus.DELETED);
			commentDAO.save(comment);
			entry.setCommentCount(entry.getCommentCount() - 1);
			entryDAO.save(entry);
			
			updateEntity(owner);
			userDAO.save(owner);
			
			return SaveResult.SUCCESS;
		} catch (ObjectRetrievalFailureException e) {
			logger.error(e);
			return SaveResult.FAIL;
		} catch (Exception e) {
			logger.error(e);
			return SaveResult.FAIL;
		}
	}

	public int updateCommentOfGoalEntry(D dto) {
		if (dto == null)
			return SaveResult.FAIL;

		try {
			C comment = commentDAO.get(dto.getId());
			dto.copyAttributesTo(comment);
			commentDAO.save(comment);
			
			User owner = comment.getUser();
			updateEntity(owner);
			userDAO.save(owner);
			
			return SaveResult.SUCCESS;
		} catch (ObjectRetrievalFailureException e) {
			logger.error(e);
		} catch (Exception e) {
			logger.error(e);
		}
		return SaveResult.FAIL;
	}

	public D get(Long id) {
		C comment = commentDAO.get(id);
		D dto = initDTO();
		dto.initFrom(comment);
		return dto;
	}

	public List<D> getAll() {
		List<C> comments = commentDAO.getAll();
		List<D> result = new ArrayList<D>();
		for(C comment : comments){
			D dto = initDTO();
			dto.initFrom(comment);
			result.add(dto);
		}
		
		return result;
	}

	public void remove(Long id) {
		commentDAO.remove(id);
	}

	public D save(D object) {
		C comment = initEntity();
		object.copyAttributesTo(comment);		
		comment = commentDAO.save(comment);
		object.initFrom(comment);
		return object;
	}

	public boolean exists(Long id) {
		return commentDAO.exists(id);
	}
	
	public abstract D initDTO();
	
	public abstract C initEntity();
}
